// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

#include "tests/cefclient/renderer/client_renderer.h"

//#include <sstream>
//#include <string>
//#include <fstream>
//#include <codecvt>

#include "include/cef_crash_util.h"
#include "include/cef_dom.h"
#include "include/wrapper/cef_helpers.h"
#include "include/wrapper/cef_message_router.h"
#include "tests/cefclient/renderer/UV8Handler.h"
//#include "include/cef_thread.h"
//#include "include/cef_parser.h"


namespace client {
namespace renderer {

namespace {

	//static void find_and_replace(std::string& source, std::string const& find, std::string const& replace)
	//{
	//	for (std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;)
	//	{
	//		source.replace(i, find.length(), replace);
	//		i += replace.length();
	//	}
	//}

	//static void wfind_and_replace(std::wstring& source, std::wstring const& find, std::wstring const& replace)
	//{
	//	for (std::wstring::size_type i = 0; (i = source.find(find, i)) != std::wstring::npos;)
	//	{
	//		source.replace(i, find.length(), replace);
	//		i += replace.length();
	//	}
	//}
// Must match the value in client_handler.cc.
const char kFocusedNodeChangedMessage[] = "ClientRenderer.FocusedNodeChanged";

class ClientRenderDelegate : public ClientAppRenderer::Delegate, CefV8Handler {
 public:
  ClientRenderDelegate() : last_node_is_editable_(false) { }

  void OnRenderThreadCreated(CefRefPtr<ClientAppRenderer> app,
                             CefRefPtr<CefListValue> extra_info) OVERRIDE {
    if (CefCrashReportingEnabled()) {
      // Set some crash keys for testing purposes. Keys must be defined in the
      // "crash_reporter.cfg" file. See cef_crash_util.h for details.
      CefSetCrashKeyValue("testkey1", "value1_renderer");
      CefSetCrashKeyValue("testkey2", "value2_renderer");
      CefSetCrashKeyValue("testkey3", "value3_renderer");
    }
  }

  void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) OVERRIDE {
    // Create the renderer-side router for query handling.
    CefMessageRouterConfig config;
    message_router_ = CefMessageRouterRendererSide::Create(config);
  }

  void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
                        CefRefPtr<CefBrowser> browser,
                        CefRefPtr<CefFrame> frame,
                        CefRefPtr<CefV8Context> context) OVERRIDE {
    message_router_->OnContextCreated(browser, frame, context);
	// Retrieve the context's window object.
	CefRefPtr<CefV8Value> object = context->GetGlobal();

	// Create a new V8 string value. See the "Basic JS Types" section below.
	CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");
	// Add the string to the window object as "window.myval". See the "JS Objects" section below.
	object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);

	CefRefPtr<CefV8Handler> myhandler = this;
	CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", myhandler);
	object->SetValue("myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);
	CefRefPtr<CefV8Value> nativefunc = CefV8Value::CreateFunction("callNative", myhandler);
	object->SetValue("callNative", nativefunc, V8_PROPERTY_ATTRIBUTE_NONE);
	//object->SetValue("wallpaperRequestRandomFileForProperty", CefV8Value::CreateFunction("wallpaperRequestRandomFileForProperty", myhandler), V8_PROPERTY_ATTRIBUTE_NONE);

	browser_ = browser;

	//CefRefPtr<UV8Handler> handler2 = new UV8Handler();
	object->SetValue("registercb",
		CefV8Value::CreateFunction("registercb", myhandler),
		V8_PROPERTY_ATTRIBUTE_NONE); 

	//object->SetValue("wallpaperRegisterAudioListener",
	//	CefV8Value::CreateFunction("wallpaperRegisterAudioListener", myhandler),
	//	V8_PROPERTY_ATTRIBUTE_NONE);

	//object->SetValue("wallpaperPropertyListener",
	//	CefV8Value::CreateFunction("wallpaperPropertyListener", myhandler),
	//	V8_PROPERTY_ATTRIBUTE_NONE);

  }

  virtual bool Execute(const CefString& name,
	  CefRefPtr<CefV8Value> object,
	  const CefV8ValueList& arguments,
	  CefRefPtr<CefV8Value>& retval,
	  CefString& exception) OVERRIDE {

	  //int argn = arguments.size();
	  if (name.compare("myfunc") == 0 ) {

		  return true;

	  } else if (name.compare("callNative") == 0) {
		  // Return my string value.
		  //retval = CefV8Value::CreateString("My Value!");
		  //int  cmd = arguments[0]->GetIntValue();
		   
		  if (arguments.size() < 2) {
			  CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("jscallnative");
			  // Retrieve the argument list object.
			  CefRefPtr<CefListValue> args = msg->GetArgumentList();
			  args->SetInt(0, -1);
			  args->SetString(1, "param error");
			  if (browser_ != NULL) {
				  browser_->SendProcessMessage(PID_BROWSER, msg);
			  }
			  return true;
		  }
		  CefRefPtr<CefV8Value> cmdvalue = arguments[0];
		  int cmd  = cmdvalue->GetIntValue();
		  //if (cmd == 100) { // here, receive wallpaper audio perperties request
			 // if (js_auct_init_callback_ == NULL ||  !js_auct_init_callback_->IsFunction())
			 //    js_auct_init_callback_ = object->GetValue("wallpaperPropertyListener")->GetValue("applyUserProperties");
			 // return true;
		  //}

		  CefRefPtr<CefV8Value>  rv = arguments[1];
		  CefString param = rv->GetStringValue();
		  // Create the message object.
		  CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("jscallnative");
		  // Retrieve the argument list object.
		  CefRefPtr<CefListValue> args = msg->GetArgumentList();
		  args->SetInt(0, cmd);
		  args->SetString(1, param);
		  if (browser_ != NULL) {
			  browser_->SendProcessMessage(PID_BROWSER, msg);
		  }
		  return true;
	  }
	  else  if (name.compare("registercb") == 0) {
		  if (arguments.size() == 1 && arguments[0]->IsFunction()) {
			  CefString fun_name = arguments[0]->GetFunctionName();
			  if (fun_name.ToString().compare("calljs") == 0) {
				  js_callback_ = arguments[0].get();
				  js_context_ = CefV8Context::GetCurrentContext();
			  }	 
			  else if(fun_name.ToString().compare("parentCalljs") == 0) {
				  js_parent_context_ = CefV8Context::GetCurrentContext();
				  js_parent_callback_ = arguments[0].get();
			  }
			  else if (fun_name.ToString().compare("localPaperCalljs") == 0) {
				  js_paper_context_ = CefV8Context::GetCurrentContext();
				  js_paper_callback_ = arguments[0].get();
			  }
			  else if (fun_name.ToString().compare("IndexPaperCalljs") == 0) {
				  js_index_context_ = CefV8Context::GetCurrentContext();
				  js_index_callback_ = arguments[0].get();
			  }
			  else if (fun_name.ToString().compare("uploadjs") == 0) {
				  js_upload_context_ = CefV8Context::GetCurrentContext();
				  js_upload_callback_ = arguments[0].get();
			  }
			  else if (fun_name.ToString().compare("dramacalljs") == 0) {
				  js_drama_context_ = CefV8Context::GetCurrentContext();
				  js_drama_callback_ = arguments[0].get();
			  }
		  }
		  return true;
	  }                             
	  /*
	  else  if (name.compare("wallpaperRegisterAudioListener") == 0) {
		  if (arguments.size() == 1 && arguments[0]->IsFunction()) {
			  arguments[0]->GetFunctionName();
			  js_auct_callback_ = arguments[0].get();  
			  js_auct_context_ = CefV8Context::GetCurrentContext();
		  }

		  CefString url = browser_->GetMainFrame()->GetURL();
		  std::wstring surl = url.ToWString();
		  int lastcha = surl.find_last_of(L"/");
		  std::wstring  purl = surl.substr(0, lastcha + 1);
		  if (  surl.substr(0,4).compare(L"file")  == 0 ) {
			  purl = purl.substr(8, surl.length() - 8);
			  wfind_and_replace(purl, L"/", L"\\\\");
		  }
		  
		  purl.append(L"project.json");

		  CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("jscallnative");
		  CefRefPtr<CefListValue> args = msg->GetArgumentList();
		  args->SetInt(0, 26);
		  CefString s1;
		  s1.FromWString(purl);
		  args->SetString(1, s1);
		  browser_->SendProcessMessage(PID_BROWSER, msg);
		  js_auct_init_callback_ = object->GetValue("wallpaperPropertyListener")->GetValue("applyUserProperties");
		  
		  return true;
	  } else  if (name.compare("wallpaperRequestRandomFileForProperty") == 0) {
		  CefV8ValueList args;
		  args.push_back(CefV8Value::CreateString(" "));
		  arguments[1].get()->ExecuteFunctionWithContext(CefV8Context::GetCurrentContext(), NULL, args);
		  return true;
	  }
	  */
	  // Function does not exist.
	  return false;
  }

  void OnContextReleased(CefRefPtr<ClientAppRenderer> app,
                         CefRefPtr<CefBrowser> browser,
                         CefRefPtr<CefFrame> frame,
                         CefRefPtr<CefV8Context> context) OVERRIDE {
    message_router_->OnContextReleased(browser, frame, context);
	//CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("jscallnative");
	//CefRefPtr<CefListValue> args = msg->GetArgumentList();
	//args->SetInt(0, 25); 
	//browser_->SendProcessMessage(PID_BROWSER, msg);
  }

  void OnFocusedNodeChanged(CefRefPtr<ClientAppRenderer> app,
                            CefRefPtr<CefBrowser> browser,
                            CefRefPtr<CefFrame> frame,
                            CefRefPtr<CefDOMNode> node) OVERRIDE {
    bool is_editable = (node.get() && node->IsEditable());
    if (is_editable != last_node_is_editable_) {
      // Notify the browser of the change in focused element type.
      last_node_is_editable_ = is_editable;
      CefRefPtr<CefProcessMessage> message =
          CefProcessMessage::Create(kFocusedNodeChangedMessage);
      message->GetArgumentList()->SetBool(0, is_editable);
      browser->SendProcessMessage(PID_BROWSER, message);
    }
  }


  bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
                                CefRefPtr<CefBrowser> browser,
                                CefProcessId source_process,
                                CefRefPtr<CefProcessMessage> message) OVERRIDE {

	  const std::string& message_name = message->GetName();
	  // MessageBox(NULL, L" TEST", L"EditWndProc  receive message", MB_OK);
	  if (message_name == "calljs" ) {
		  //browser->GetMainFrame()->ExecuteJavaScript("alert('ExecuteJavaScript works!');",
			 // browser->GetMainFrame()->GetURL(), 0);
		  if (js_callback_) { 
			  CefV8ValueList args;
			  int cmd = message->GetArgumentList()->GetInt(0);
			  CefString str = message->GetArgumentList()->GetString(1);
			  args.push_back(CefV8Value::CreateInt(cmd));
			  args.push_back(CefV8Value::CreateString(str)); 
			  js_callback_->ExecuteFunctionWithContext(js_context_, NULL, args);
		  }

		  return true;
	  }
	  else	  if (message_name == "localPaperCalljs") {
		  if (js_paper_callback_) {
			  CefV8ValueList args;
			  int cmd = message->GetArgumentList()->GetInt(0);
			  CefString str = message->GetArgumentList()->GetString(1);
			  args.push_back(CefV8Value::CreateInt(cmd));
			  args.push_back(CefV8Value::CreateString(str));
			  js_paper_callback_->ExecuteFunctionWithContext(js_paper_context_, NULL, args);
		  }

		  return true;
	  }
	  else	  if (message_name == "parentCalljs") {
		  if (js_parent_callback_) {
			  CefV8ValueList args;
			  int cmd = message->GetArgumentList()->GetInt(0);
			  CefString str = message->GetArgumentList()->GetString(1);
			  args.push_back(CefV8Value::CreateInt(cmd));
			  args.push_back(CefV8Value::CreateString(str));
			  js_parent_callback_->ExecuteFunctionWithContext(js_parent_context_, NULL, args);
		  }
		  return true;
	  }
	  else	  if (message_name == "IndexPaperCalljs") {
		  if (js_parent_callback_) {
			  CefV8ValueList args;
			  int cmd = message->GetArgumentList()->GetInt(0);
			  CefString str = message->GetArgumentList()->GetString(1);
			  args.push_back(CefV8Value::CreateInt(cmd));
			  args.push_back(CefV8Value::CreateString(str));
			  js_index_callback_->ExecuteFunctionWithContext(js_index_context_, NULL, args);
		  }
		  return true;
	  }
	  else	  if (message_name == "uploadjs") {
		  if (js_upload_callback_) {
			  CefV8ValueList args;
			  int cmd = message->GetArgumentList()->GetInt(0);
			  CefString str = message->GetArgumentList()->GetString(1);
			  args.push_back(CefV8Value::CreateInt(cmd));
			  args.push_back(CefV8Value::CreateString(str));
			  js_upload_callback_->ExecuteFunctionWithContext(js_upload_context_, NULL, args);
		  }
		  return true;
	  }
	  else	  if (message_name == "dramacalljs") {
		  if (js_drama_callback_) {
			  CefV8ValueList args;
			  int cmd = message->GetArgumentList()->GetInt(0);
			  CefString str = message->GetArgumentList()->GetString(1);
			  args.push_back(CefV8Value::CreateInt(cmd));
			  args.push_back(CefV8Value::CreateString(str));
			  js_drama_callback_->ExecuteFunctionWithContext(js_drama_context_, NULL, args);
		  }
		  return true;
	  }
	  
	  /*
	  else	  if (message_name == "callaudiojs") { 	  
		  if (js_auct_callback_) {
			  CefV8ValueList arguments;
			  CefRefPtr<CefV8Value> retval;
			  CefRefPtr<CefV8Exception> exception;
 
			  int cmd = message->GetArgumentList()->GetInt(0);
 
			  if (cmd == 1) {  // Send audio captured to js
				  CefString  audata = message->GetArgumentList()->GetString(1);
				  arguments.push_back(CefV8Value::CreateString(audata));
				  if (false) {
					  CefRefPtr<CefV8Value>  args = CefV8Value::CreateArray(2);
					  args->SetValue(0, CefV8Value::CreateString("jhfaksd"));
					  args->SetValue(1, CefV8Value::CreateString("jhfaksd"));
					  arguments.push_back(args);
				  }
				  js_auct_callback_->ExecuteFunctionWithContext(js_auct_context_, NULL, arguments);
			  } else if (cmd == 2) {  // Execute audio properties init	 
				  if (js_auct_init_callback_ != NULL && js_auct_init_callback_->IsFunction()) {
					  CefString  audata = message->GetArgumentList()->GetString(1);
					  arguments.push_back(CefV8Value::CreateString(audata));
					  js_auct_init_callback_->ExecuteFunctionWithContext(js_auct_context_, NULL, arguments);
				  }			   
			  }
		  }
		  return true;
	  } */
    return message_router_->OnProcessMessageReceived(browser, source_process,
                                                     message);
  }

  /*
  void  parseproperties(std::wstring propertypath, CefRefPtr<CefV8Value> obj) {  
	  std::ifstream t(propertypath);
	  if (!t.good()) { 		 
		  CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("jscallnative");
		  CefRefPtr<CefListValue> args = msg->GetArgumentList();
		  args->SetInt(0, 103);
		  std::wstring s;
		  s.append(L"can not open file [");
		  s.append(propertypath);
		  s.append(L"]");
		  CefString s1;
		  s1.FromWString(s);
		  args->SetString(1,s1);
		  if (browser_ != NULL) {
			  browser_->SendProcessMessage(PID_BROWSER, msg);
		  }
		  return;
	  }
	  std::string str((std::istreambuf_iterator<char>(t)),
		  std::istreambuf_iterator<char>());
	  t.close(); 
	  CefString  content;
	  content.FromString(str);
	  CefRefPtr<CefValue> jsonObject = CefParseJSON(content, JSON_PARSER_ALLOW_TRAILING_COMMAS);
	  if (jsonObject->IsValid())
	  {
		  CefRefPtr<CefDictionaryValue> dictall = jsonObject->GetDictionary();

		  CefRefPtr<CefDictionaryValue> dict = dictall->GetDictionary("general")->GetDictionary("properties");

		  if (dict->HasKey("ampall")) {
			  CefRefPtr<CefDictionaryValue> dictampall = dict->GetDictionary("ampall");
			  CefRefPtr<CefV8Value>  ampallobj = CefV8Value::CreateObject(NULL, NULL);
			  ampallobj->SetValue("condition", CefV8Value::CreateString(dictampall->GetString("condition")),   V8_PROPERTY_ATTRIBUTE_NONE);
			  ampallobj->SetValue("max", CefV8Value::CreateInt(dictampall->GetInt("max")), V8_PROPERTY_ATTRIBUTE_NONE);
			  ampallobj->SetValue("min", CefV8Value::CreateInt(dictampall->GetInt("min")), V8_PROPERTY_ATTRIBUTE_NONE);
			  ampallobj->SetValue("order", CefV8Value::CreateInt(dictampall->GetInt("order")), V8_PROPERTY_ATTRIBUTE_NONE);
			  ampallobj->SetValue("text", CefV8Value::CreateString(dictampall->GetString("text")), V8_PROPERTY_ATTRIBUTE_NONE);
			  ampallobj->SetValue("type", CefV8Value::CreateString(dictampall->GetString("type")), V8_PROPERTY_ATTRIBUTE_NONE);
			  ampallobj->SetValue("value", CefV8Value::CreateInt(dictampall->GetInt("value")), V8_PROPERTY_ATTRIBUTE_NONE);
			  obj->SetValue("ampall", ampallobj, V8_PROPERTY_ATTRIBUTE_NONE);
		  }		   
	  }
	  else { 
	  }
  }
  */
 private:
  bool last_node_is_editable_;
  CefRefPtr<CefV8Context>  js_context_;
  CefRefPtr<CefV8Value>  js_callback_;
  CefRefPtr<CefV8Context>  js_index_context_;
  CefRefPtr<CefV8Value>  js_index_callback_;
  CefRefPtr<CefV8Context>  js_parent_context_;
  CefRefPtr<CefV8Value>  js_parent_callback_;
  CefRefPtr<CefV8Context>  js_paper_context_;
  CefRefPtr<CefV8Value>  js_paper_callback_;
  CefRefPtr<CefV8Context>  js_auct_context_;
  CefRefPtr<CefV8Value>  js_upload_callback_;
  CefRefPtr<CefV8Context>  js_upload_context_;
  CefRefPtr<CefV8Value>  js_drama_callback_;
  CefRefPtr<CefV8Context>  js_drama_context_; 
  CefRefPtr<CefMessageRouterRendererSide> message_router_;

  CefRefPtr<CefBrowser> browser_; 

  DISALLOW_COPY_AND_ASSIGN(ClientRenderDelegate);
  IMPLEMENT_REFCOUNTING(ClientRenderDelegate);
};

}  // namespace

void CreateDelegates(ClientAppRenderer::DelegateSet& delegates) {
  delegates.insert(new ClientRenderDelegate);
}

}  // namespace renderer
}  // namespace client
